#ifndef _LOAD_COM_HPP_
#define _LOAD_COM_HPP_

#include <dlcom/unknown.h>
#include <utilex/dllmodule.hpp>

class CComLoader : protected CDllmodule
{
public:
    DefineDllGetClassObject DllGetClassObject;
    DefineDllGetAt DllGetAt;
    DefineDllGetCount DllGetCount;
    DefineDllProgIDFromCLSID DllProgIDFromCLSID;
    DefineDllCanUnloadNow DllCanUnloadNow;
    DefineDllRegisterServer DllRegisterServer;
    DefineDllUnregisterServer DllUnregisterServer;
    DefineDllStart DllStart;
    DefineDllStop DllStop;
    DefineDllSetup DllSetup;
    DefineDllGetVersion DllGetVersion;

public:
    inline CComLoader() { }
    virtual STFunDesc* GetFunDefs()
    {
        static STFunDesc s_FunDefs[] = {
            {(char*)DllGetClassObjectFuncName, 0},
            {(char*)DllGetAtFuncName, 1},
            {(char*)DllGetCountFuncName, 2},
            {(char*)DllProgIDFromCLSIDFuncName, 3},
            {(char*)DllCanUnloadNowFuncName, 4},
            {(char*)DllRegisterServerFuncName, 5},
            {(char*)DllUnregisterServerFuncName, 6},
            {(char*)DllStartFuncName, 7},
            {(char*)DllStopFuncName, 8},
            {(char*)DllSetupFuncName, 9},
            {(char*)DllGetVersionFuncName, 10},
            {(char*)NULLFuncName, 11}};
        return s_FunDefs;
    }
    virtual bool SetFuncAddress(size_t i, void* p)
    {
        switch (i) {
        case 0:
            this->DllGetClassObject = (DefineDllGetClassObject)(p);
            break;
        case 1:
            this->DllGetAt = (DefineDllGetAt)(p);
            break;
        case 2:
            this->DllGetCount = (DefineDllGetCount)(p);
            break;
        case 3:
            this->DllProgIDFromCLSID = (DefineDllProgIDFromCLSID)(p);
            break;
        case 4:
            this->DllCanUnloadNow = (DefineDllCanUnloadNow)(p);
            break;
        case 5:
            this->DllRegisterServer = (DefineDllRegisterServer)(p);
            break;
        case 6:
            this->DllUnregisterServer = (DefineDllUnregisterServer)(p);
            break;
        case 7:
            this->DllStart = (DefineDllStart)(p);
            break;
        case 8:
            this->DllStop = (DefineDllStop)(p);
            break;
        case 9:
            this->DllSetup = (DefineDllSetup)(p);
            break;
        case 10:
            this->DllGetVersion = (DefineDllGetVersion)(p);
            break;
        default:
            break;
        }
        return true;
    }
    virtual ~CComLoader() { UnloadCom(); }

public:
    HRESULT LoadCom(LPSTRING pModPathName)
    {
        tmemset(m_szModuleName, 0, MAX_PATH);
        tstring_strcpy(m_szModuleName, MAX_PATH, pModPathName);

        int ret = Load();

        if (ret != S_SUCCESS) {
            debug_view("CComLoader->Load fuc fail") wdebug_view(m_szModuleName);
            tmemset(m_szModuleName, 0, MAX_PATH);
            return E_FAIL;
        }

        if (DllGetClassObject == NULL) {
            debug_view("CComLoader->DllGetClassObject error") return E_FAIL;
        }

        return S_OK;
    }
    virtual void UnloadCom() { UnLoad(); }
    const basic_tchar* GetComPath() { return m_szModuleName; }
    HRESULT
    CreateInstance(REFCLSID rclsid, IBase* prot, IBase* punkOuter, const IID& riid, void** ppv)
    {
        rc_error(IsLoaded(), E_FAIL);

        _lComPtr<IClsFactory> pClsFactory;
        pClsFactory.dispose();

        if (S_SUCCEEDED(DllGetClassObject(rclsid, IID_IClsFactory, (void**)&pClsFactory)) && pClsFactory) {
            return pClsFactory->CreateInstance(punkOuter, riid, ppv);
        }
        return E_FAIL;
    }
    LONG GetCount() { return this->DllGetCount(); }
    CLSID GetAt(LONG nIndex) { return this->DllGetAt(nIndex); }
    LPCSTR ProgIDFromCLSID(REFCLSID clsid) { return this->DllProgIDFromCLSID(clsid); }
    HRESULT Setup(UINT type, UINT version) { return this->DllSetup(type, version); }
    int IsLoaded() { return CDllmodule::IsLoaded(); }
};

#endif
